home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2007 January, February, March & April
/
Chip-Cover-CD-2007-02.iso
/
Pakiet multimedia
/
Muzyka
/
Edytory sampli (probek dzwieku)
/
ZynAddSubFX_2.2.0
/
Setup_ZynAddSubFX-2.2.0.exe
/
source code
/
Misc
/
Microtonal.C
< prev
next >
Wrap
C/C++ Source or Header
|
2005-03-14
|
15KB
|
515 lines
/*
ZynAddSubFX - a software synthesizer
Microtonal.C - Tuning settings and microtonal capabilities
Copyright (C) 2002-2005 Nasca Octavian Paul
Author: Nasca Octavian Paul
This program is free software; you can redistribute it and/or modify
it under the terms of version 2 of the GNU General Public License
as published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License (version 2) for more details.
You should have received a copy of the GNU General Public License (version 2)
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <math.h>
#include <string.h>
#include "Microtonal.h"
#define MAX_LINE_SIZE 80
Microtonal::Microtonal(){
Pname=new unsigned char[MICROTONAL_MAX_NAME_LEN];
Pcomment=new unsigned char[MICROTONAL_MAX_NAME_LEN];
defaults();
};
void Microtonal::defaults(){
Pinvertupdown=0;
Pinvertupdowncenter=60;
octavesize=12;
Penabled=0;
PAnote=69;
PAfreq=440.0;
Pscaleshift=64;
Pfirstkey=0;Plastkey=127;
Pmiddlenote=60;Pmapsize=12;
Pmappingenabled=0;
for (int i=0;i<128;i++) Pmapping[i]=i;
for (int i=0;i<MAX_OCTAVE_SIZE;i++){
octave[i].tuning=tmpoctave[i].tuning=pow(2,(i%octavesize+1)/12.0);
octave[i].type=tmpoctave[i].type=1;
octave[i].x1=tmpoctave[i].x1=(i%octavesize+1)*100;
octave[i].x2=tmpoctave[i].x2=0;
};
octave[11].type=2;octave[11].x1=2;octave[11].x2=1;
for (int i=0;i<MICROTONAL_MAX_NAME_LEN;i++){
Pname[i]='\0';
Pcomment[i]='\0';
};
snprintf((char *) Pname,MICROTONAL_MAX_NAME_LEN,"12tET");
snprintf((char *) Pcomment,MICROTONAL_MAX_NAME_LEN,"Equal Temperament 12 notes per octave");
Pglobalfinedetune=64;
};
Microtonal::~Microtonal(){
delete (Pname);
delete (Pcomment);
};
/*
* Get the size of the octave
*/
unsigned char Microtonal::getoctavesize(){
if (Penabled!=0) return(octavesize);
else return(12);
};
/*
* Get the frequency according the note number
*/
REALTYPE Microtonal::getnotefreq(int note,int keyshift){
// in this function will appears many times things like this:
// var=(a+b*100)%b
// I had written this way because if I use var=a%b gives unwanted results when a<0
// This is the same with divisions.
if ((Pinvertupdown!=0)&&((Pmappingenabled==0)||(Penabled==0))) note=(int) Pinvertupdowncenter*2-note;
//compute global fine detune
REALTYPE globalfinedetunerap=pow(2.0,(Pglobalfinedetune-64.0)/1200.0);//-64.0 .. 63.0 cents
if (Penabled==0) return(pow(2.0,(note-PAnote+keyshift)/12.0)*PAfreq*globalfinedetunerap);//12tET
int scaleshift=((int)Pscaleshift-64+(int) octavesize*100)%octavesize;
//compute the keyshift
REALTYPE rap_keyshift=1.0;
if (keyshift!=0){
int kskey=(keyshift+(int)octavesize*100)%octavesize;
int ksoct=(keyshift+(int)octavesize*100)/octavesize-100;
rap_keyshift=(kskey==0) ? (1.0):(octave[kskey-1].tuning);
rap_keyshift*=pow(octave[octavesize-1].tuning,ksoct);
};
//if the mapping is enabled
if (Pmappingenabled!=0){
if ((note<Pfirstkey)||(note>Plastkey)) return (-1.0);
//Compute how many mapped keys are from middle note to reference note
//and find out the proportion between the freq. of middle note and "A" note
int tmp=PAnote-Pmiddlenote,minus=0;
if (tmp<0) { tmp=-tmp; minus=1; };
int deltanote=0;
for (int i=0;i<tmp;i++) if (Pmapping[i%Pmapsize]>=0) deltanote++;
REALTYPE rap_anote_middlenote=(deltanote==0) ? (1.0) : (octave[(deltanote-1)%octavesize].tuning);
if (deltanote!=0) rap_anote_middlenote*=pow(octave[octavesize-1].tuning,(deltanote-1)/octavesize);
if (minus!=0) rap_anote_middlenote=1.0/rap_anote_middlenote;
//Convert from note (midi) to degree (note from the tunning)
int degoct=(note-(int)Pmiddlenote+(int) Pmapsize*200)/(int)Pmapsize-200;
int degkey=(note-Pmiddlenote+(int)Pmapsize*100)%Pmapsize;
degkey=Pmapping[degkey];
if (degkey<0) return(-1.0);//this key is not mapped
//invert the keyboard upside-down if it is asked for
//TODO: do the right way by using Pinvertupdowncenter
if (Pinvertupdown!=0){
degkey=octavesize-degkey-1;
degoct=-degoct;
};
//compute the frequency of the note
degkey=degkey+scaleshift;
degoct+=degkey/octavesize;
degkey%=octavesize;
REALTYPE freq=(degkey==0) ? (1.0):octave[degkey-1].tuning;
freq*=pow(octave[octavesize-1].tuning,degoct);
freq*=PAfreq/rap_anote_middlenote;
freq*=globalfinedetunerap;
if (scaleshift!=0) freq/=octave[scaleshift-1].tuning;
return(freq*rap_keyshift);
} else {//if the mapping is disabled
int nt=note-PAnote+scaleshift;
int ntkey=(nt+(int)octavesize*100)%octavesize;
int ntoct=(nt-ntkey)/octavesize;
REALTYPE oct=octave[octavesize-1].tuning;
REALTYPE freq=octave[(ntkey+octavesize-1)%octavesize].tuning*pow(oct,ntoct)*PAfreq;
if (ntkey==0) freq/=oct;
if (scaleshift!=0) freq/=octave[scaleshift-1].tuning;
// fprintf(stderr,"note=%d freq=%.3f cents=%d\n",note,freq,(int)floor(log(freq/PAfreq)/log(2.0)*1200.0+0.5));
freq*=globalfinedetunerap;
return(freq*rap_keyshift);
};
};
/*
* Convert a line to tunings; returns -1 if it ok
*/
int Microtonal::linetotunings(unsigned int nline,const char *line){
int x1=-1,x2=-1,type=-1;
REALTYPE x=-1.0,tmp,tuning=1.0;
if (strstr(line,"/")==NULL){
if (strstr(line,".")==NULL){// M case (M=M/1)
sscanf(line,"%d",&x1);
x2=1;
type=2;//division
} else {// float number case
sscanf(line,"%f",&x);
if (x<0.000001) return(1);
type=1;//float type(cents)
};
} else {// M/N case
sscanf(line,"%d/%d",&x1,&x2);
if ((x1<0)||(x2<0)) return(1);
if (x2==0) x2=1;
type=2;//division
};
if (x1<=0) x1=1;//not allow zero frequency sounds (consider 0 as 1)
//convert to float if the number are too big
if ((type==2)&&((x1>(128*128*128-1))||(x2>(128*128*128-1)))){
type=1;
x=((REALTYPE) x1)/x2;
};
switch (type){
case 1: x1=(int) floor(x);
tmp=fmod(x,1.0);
x2=(int) (floor (tmp*1e6));
tuning=pow(2.0,x/1200.0);
break;
case 2: x=((REALTYPE)x1)/x2;
tuning=x;
break;
};
tmpoctave[nline].tuning=tuning;
tmpoctave[nline].type=type;
tmpoctave[nline].x1=x1;
tmpoctave[nline].x2=x2;
return(-1);//ok
};
/*
* Convert the text to tunnings
*/
int Microtonal::texttotunings(const char *text){
unsigned int i,k=0,nl=0;
char *lin;
lin=new char[MAX_LINE_SIZE+1];
while (k<strlen(text)){
for (i=0;i<MAX_LINE_SIZE;i++){
lin[i]=text[k++];
if (lin[i]<0x20) break;
};
lin[i]='\0';
if (strlen(lin)==0) continue;
int err=linetotunings(nl,lin);
if (err!=-1) {
delete [] lin;
return(nl);//Parse error
};
nl++;
};
delete [] lin;
if (nl>MAX_OCTAVE_SIZE) nl=MAX_OCTAVE_SIZE;
if (nl==0) return(-2);//the input is empty
octavesize=nl;
for (i=0;i<octavesize;i++){
octave[i].tuning=tmpoctave[i].tuning;
octave[i].type=tmpoctave[i].type;
octave[i].x1=tmpoctave[i].x1;
octave[i].x2=tmpoctave[i].x2;
};
return(-1);//ok
};
/*
* Convert the text to mapping
*/
void Microtonal::texttomapping(const char *text){
unsigned int i,k=0;
char *lin;
lin=new char[MAX_LINE_SIZE+1];
for (i=0;i<128;i++) Pmapping[i]=-1;
int tx=0;
while (k<strlen(text)){
for (i=0;i<MAX_LINE_SIZE;i++){
lin[i]=text[k++];
if (lin[i]<0x20) break;
};
lin[i]='\0';
if (strlen(lin)==0) continue;
int tmp=0;
if (sscanf(lin,"%d",&tmp)==0) tmp=-1;
if (tmp<-1) tmp=-1;
Pmapping[tx]=tmp;
if ((tx++)>127) break;
};
delete [] lin;
if (tx==0) tx=1;
Pmapsize=tx;
};
/*
* Convert tunning to text line
*/
void Microtonal::tuningtoline(int n,char *line,int maxn){
if ((n>octavesize) || (n>MAX_OCTAVE_SIZE)) {
line[0]='\0';
return;
};
if (octave[n].type==1) snprintf(line,maxn,"%d.%d",octave[n].x1,octave[n].x2);
if (octave[n].type==2) snprintf(line,maxn,"%d/%d",octave[n].x1,octave[n].x2);
};
int Microtonal::loadline(FILE *file,char *line){
do {
if (fgets(line,500,file)==0) return(1);
} while (line[0]=='!');
return(0);
};
/*
* Loads the tunnings from a scl file
*/
int Microtonal::loadscl(const char *filename){
FILE *file=fopen(filename, "r");
char tmp[500];
fseek(file,0,SEEK_SET);
//loads the short description
if (loadline(file,&tmp[0])!=0) return(2);
for (int i=0;i<500;i++) if (tmp[i]<32) tmp[i]=0;
snprintf((char *) Pname,MICROTONAL_MAX_NAME_LEN,"%s",tmp);
snprintf((char *) Pcomment,MICROTONAL_MAX_NAME_LEN,"%s",tmp);
//loads the number of the notes
if (loadline(file,&tmp[0])!=0) return(2);
int nnotes=MAX_OCTAVE_SIZE;
sscanf(&tmp[0],"%d",&nnotes);
if (nnotes>MAX_OCTAVE_SIZE) return (2);
//load the tunnings
for (int nline=0;nline<nnotes;nline++){
if (loadline(file,&tmp[0])!=0) return(2);
linetotunings(nline,&tmp[0]);
};
fclose(file);
octavesize=nnotes;
for (int i=0;i<octavesize;i++){
octave[i].tuning=tmpoctave[i].tuning;
octave[i].type=tmpoctave[i].type;
octave[i].x1=tmpoctave[i].x1;
octave[i].x2=tmpoctave[i].x2;
};
return(0);
};
/*
* Loads the mapping from a kbm file
*/
int Microtonal::loadkbm(const char *filename){
FILE *file=fopen(filename, "r");
int x;
char tmp[500];
fseek(file,0,SEEK_SET);
//loads the mapsize
if (loadline(file,&tmp[0])!=0) return(2);
if (sscanf(&tmp[0],"%d",&x)==0) return(2);
if (x<1) x=0;if (x>127) x=127;//just in case...
Pmapsize=x;
//loads first MIDI note to retune
if (loadline(file,&tmp[0])!=0) return(2);
if (sscanf(&tmp[0],"%d",&x)==0) return(2);
if (x<1) x=0;if (x>127) x=127;//just in case...
Pfirstkey=x;
//loads last MIDI note to retune
if (loadline(file,&tmp[0])!=0) return(2);
if (sscanf(&tmp[0],"%d",&x)==0) return(2);
if (x<1) x=0;if (x>127) x=127;//just in case...
Plastkey=x;
//loads last the middle note where scale fro scale degree=0
if (loadline(file,&tmp[0])!=0) return(2);
if (sscanf(&tmp[0],"%d",&x)==0) return(2);
if (x<1) x=0;if (x>127) x=127;//just in case...
Pmiddlenote=x;
//loads the reference note
if (loadline(file,&tmp[0])!=0) return(2);
if (sscanf(&tmp[0],"%d",&x)==0) return(2);
if (x<1) x=0;if (x>127) x=127;//just in case...
PAnote=x;
//loads the reference freq.
if (loadline(file,&tmp[0])!=0) return(2);
REALTYPE tmpPAfreq=440.0;
if (sscanf(&tmp[0],"%f",&tmpPAfreq)==0) return(2);
PAfreq=tmpPAfreq;
//the scale degree(which is the octave) is not loaded, it is obtained by the tunnings with getoctavesize() method
if (loadline(file,&tmp[0])!=0) return(2);
//load the mappings
if (Pmapsize!=0){
for (int nline=0;nline<Pmapsize;nline++){
if (loadline(file,&tmp[0])!=0) return(2);
if (sscanf(&tmp[0],"%d",&x)==0) x=-1;
Pmapping[nline]=x;
};
Pmappingenabled=1;
} else {
Pmappingenabled=0;
Pmapping[0]=0;
Pmapsize=1;
};
fclose(file);
return(0);
};
void Microtonal::add2XML(XMLwrapper *xml){
xml->addparstr("name",(char *) Pname);
xml->addparstr("comment",(char *) Pcomment);
xml->addparbool("invert_up_down",Pinvertupdown);
xml->addparbool("invert_up_down_center",Pinvertupdowncenter);
xml->addparbool("enabled",Penabled);
xml->addpar("global_fine_detune",Pglobalfinedetune);
xml->addpar("a_note",PAnote);
xml->addparreal("a_freq",PAfreq);
if ((Penabled==0)&&(xml->minimal)) return;
xml->beginbranch("SCALE");
xml->addpar("scale_shift",Pscaleshift);
xml->addpar("first_key",Pfirstkey);
xml->addpar("last_key",Plastkey);
xml->addpar("middle_note",Pmiddlenote);
xml->beginbranch("OCTAVE");
xml->addpar("octave_size",octavesize);
for (int i=0;i<octavesize;i++){
xml->beginbranch("DEGREE",i);
if (octave[i].type==1){
xml->addparreal("cents",octave[i].tuning);
};
if (octave[i].type==2){
xml->addpar("numerator",octave[i].x1);
xml->addpar("denominator",octave[i].x2);
};
xml->endbranch();
};
xml->endbranch();
xml->beginbranch("KEYBOARD_MAPPING");
xml->addpar("map_size",Pmapsize);
xml->addpar("mapping_enabled",Pmappingenabled);
for (int i=0;i<Pmapsize;i++){
xml->beginbranch("KEYMAP",i);
xml->addpar("degree",Pmapping[i]);
xml->endbranch();
};
xml->endbranch();
xml->endbranch();
};
void Microtonal::getfromXML(XMLwrapper *xml){
xml->getparstr("name",(char *) Pname,MICROTONAL_MAX_NAME_LEN);
xml->getparstr("comment",(char *) Pcomment,MICROTONAL_MAX_NAME_LEN);
Pinvertupdown=xml->getparbool("invert_up_down",Pinvertupdown);
Pinvertupdowncenter=xml->getparbool("invert_up_down_center",Pinvertupdowncenter);
Penabled=xml->getparbool("enabled",Penabled);
Pglobalfinedetune=xml->getpar127("global_fine_detune",Pglobalfinedetune);
PAnote=xml->getpar127("a_note",PAnote);
PAfreq=xml->getparreal("a_freq",PAfreq,1.0,10000.0);
if (xml->enterbranch("SCALE")){
Pscaleshift=xml->getpar127("scale_shift",Pscaleshift);
Pfirstkey=xml->getpar127("first_key",Pfirstkey);
Plastkey=xml->getpar127("last_key",Plastkey);
Pmiddlenote=xml->getpar127("middle_note",Pmiddlenote);
if (xml->enterbranch("OCTAVE")){
octavesize=xml->getpar127("octave_size",octavesize);
for (int i=0;i<octavesize;i++){
if (xml->enterbranch("DEGREE",i)==0) continue;
octave[i].x2=0;
octave[i].tuning=xml->getparreal("cents",octave[i].tuning);
octave[i].x1=xml->getpar127("numerator",octave[i].x1);
octave[i].x2=xml->getpar127("denominator",octave[i].x2);
if (octave[i].x2!=0) octave[i].type=2;
else octave[i].type=1;
xml->exitbranch();
};
xml->exitbranch();
};
if (xml->enterbranch("KEYBOARD_MAPPING")){
Pmapsize=xml->getpar127("map_size",Pmapsize);
Pmappingenabled=xml->getpar127("mapping_enabled",Pmappingenabled);
for (int i=0;i<Pmapsize;i++){
if (xml->enterbranch("KEYMAP",i)==0) continue;
Pmapping[i]=xml->getpar127("degree",Pmapping[i]);
xml->exitbranch();
};
xml->exitbranch();
};
xml->exitbranch();
};
};
int Microtonal::saveXML(char *filename){
XMLwrapper *xml=new XMLwrapper();
xml->beginbranch("MICROTONAL");
add2XML(xml);
xml->endbranch();
int result=xml->saveXMLfile(filename);
delete (xml);
return(result);
};
int Microtonal::loadXML(char *filename){
XMLwrapper *xml=new XMLwrapper();
if (xml->loadXMLfile(filename)<0) {
delete(xml);
return(-1);
};
if (xml->enterbranch("MICROTONAL")==0) return(-10);
getfromXML(xml);
xml->exitbranch();
delete(xml);
return(0);
};